闲话 22.9.30

闲话

现在是8:00
我一个字没写
我就看我什么时候能写完这博客
现在是 8:40
我水完了

关于某个奇怪的题单
某个奇怪的人突然就给我了
说“我没写题解 你要继承我的遗志 把这里面的题都写了题解”
然后 ta 就 *** 了
然后我就开贺了

于是似乎最近不闲的闲话部分有话题了
希望能一天一道
“贺就完了”,某个奇怪的人说


図鑑に載ってた宇宙の話を

毎日読んでいた

最後のページに

世界の真理があると思っていた

でも時間が経って大きくなって

だんだん大人の匂いになって

そんなことは諦めた

杂题

CF739E

你要抓神奇宝贝!现在一共有 \(n\) 只神奇宝贝。
你有 \(a\) 个『宝贝球』和 \(b\) 个『超级球』。
『宝贝球』抓到第 \(i\) 只神奇宝贝的概率是 \(p_i\),『超级球』抓到的概率则是 \(u_i\)
不能往同一只神奇宝贝上使用超过一个同种的『球』,但是可以往同一只上既使用『宝贝球』又使用『超级球』(都抓到算一个)。
请合理分配每个球抓谁,使得你抓到神奇宝贝的总个数期望最大,并输出这个值。

\(n \leq 10^5\)

solution1

考虑一个dp。平凡地,我们设 \(f[i][j][k]\) 表示抓了前 \(i\) 只神奇宝贝,用了 \(j\) 只普通球, \(k\) 只超级球。然后 \(O(n^3)\) 转移。
考虑一个优化。我们应该压掉一维,使得转移速度加快。这就让我们想到了什么?wqs二分。
经过打表 严密的证明和足够多的表 证明,这个dp在 \(k\) 维上是凸的。
证当然也可以证了:当 \(i,j\) 相同时,第一只超级球肯定抓期望最大的神奇宝贝,第二只抓次大……类推可证。
然后我们发现这个玩意在 \(j\) 维上也是凸的。

考虑wqs二分。我们首先让每个超级球有自己的花费,内层再让普通球有个自己的花费
然后wqs二分套wqs二分 写就完了

复杂度 \(O(n \log^2 n)\)

solution2

wqs二分
定义斜率k为使用超级球时多加的花费
然后二分斜率 每次扫一遍所有宝可梦,看如何选择

  1. 我都要!p + q - pq - k
  2. 只用干脆面 p
  3. 只用豆干 q - k
  4. 滚蛋! 0

于是考虑每只宝可梦什么情况下是否选超级球
max(p + q - pq - k, p) - max(q - k, 0)
即为答案的变化量
内层sort一下选最优答案就行,复杂度 \(O(n \log^2 n)\)

题解说可以分讨避免内层排序
因此做到 \(O(n \log n)\)

code[solution2]
#include <bits/stdc++.h>
#define N 100005
#define rep(i,a,b) for ( register int (i) = (a); (i) <= (b); ++(i) )
#define pre(i,a,b) for ( register int (i) = (a); (i) >= (b); --(i) )
using namespace std;
double ans, p[N], q[N];
int n, a, b;

struct node {
	int a0, a1;
	// 用第一种/第二种粮与否
	double x;
	// 在此情况下当前wqs二分扰动后的可能性
	bool operator < (const node & b) const {
		return x > b.x;
	}
} tmp[N];

bool check(double k) {
	int c = 0; ans = 0;
	rep(i,1,n) {
		if (p[i] > p[i] + q[i] - p[i] * q[i] - k) tmp[i].a1 = 0, tmp[i].x = p[i];
		else tmp[i].a1 = 1, tmp[i].x = p[i] + q[i] - p[i] * q[i] - k;
		if (q[i] - k < 0) tmp[i].a0 = 0;
		else tmp[i].a0 = 1, tmp[i].x -= q[i] - k, ans += q[i] - k;
	} sort(tmp+1, tmp+1+n);
	rep(i,1,a) c += tmp[i].a1, ans += tmp[i].x;
	rep(i,a+1, n) c += tmp[i].a0;
	return c < b;
}

signed main() {
	cin >> n >> a >> b; 
	rep(i,1,n) cin >> p[i];
	rep(i,1,n) cin >> q[i];
	double l = 0, r = 1, mid = 0;
	while (r - l >= 1e-11) {
		mid = (l + r) / 2;
		if (check(mid)) {
			r = mid;
		} else l = mid;
	} printf("%.6lf", ans + b * mid);
	return 0;
}
posted @ 2022-09-30 20:49  joke3579  阅读(45)  评论(1编辑  收藏  举报